using System;
using System.Collections.Generic;
using System.Linq;
using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Fantasy.SourceGenerator.Generators
{
    [Generator]
    public partial class EntityTypeCollectionGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 查找所有 Entity 类定义以及泛型实体的闭合使用
            var protoBufTypes = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => IsEntityTarget(node), 
                    transform: static (ctx, _) => GetEntityTypeInfo(ctx))
                .Where(static info => info != null)
                .Collect()
                .Select(static (types, _) => types.Distinct().ToList());

            // 组合编译信息和找到的类型
            var compilationAndTypes = context.CompilationProvider.Combine(protoBufTypes);

            // 源码输出
            context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
            {
                if (CompilationHelper.IsSourceGeneratorDisabled(source.Left)) return;
                if (!CompilationHelper.HasFantasyDefine(source.Left)) return;
                if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.IEntityTypeCollectionRegistrar") == null) return;
                GenerateRegistrationCode(spc, source.Left, source.Right!);
            });
        }
        
        /// <summary>
        /// 生成注册代码
        /// </summary>
        private static void GenerateRegistrationCode(
            SourceProductionContext context,
            Compilation compilation,
            IEnumerable<EntityTypeInfo> entityTypeInfos)
        {
            var entityTypeList = entityTypeInfos.ToList();
            // 获取当前程序集名称（仅用于注释）
            var markerClassName = compilation.GetAssemblyName("EntityTypeCollectionRegistrar", out var assemblyName, out _);
            var builder = new SourceCodeBuilder();
            // 添加文件头
            builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
            // 添加 using
            builder.AddUsings(
                "System",
                "System.Collections.Generic",
                "Fantasy.Assembly",
                "Fantasy.DataStructure.Collection",
                "Fantasy.Event"
            );
            builder.AppendLine();
            // 开始命名空间
             builder.BeginDefaultNamespace();
            // 开始类定义（实现 IEntityTypeCollectionRegistrar 接口）
            builder.AddXmlComment($"Automatically generated Entity Type collection class for {assemblyName}");
            builder.BeginClass(markerClassName, "internal sealed", "global::Fantasy.Assembly.IEntityTypeCollectionRegistrar");
            // 生成 GetEntityTypes 方法
            GetEntityTypesMethod(builder, entityTypeList);
            builder.AppendLine();
            // 结束类和命名空间
            builder.EndClass();
            builder.EndNamespace();
            // 输出源代码
            context.AddSource($"{markerClassName}.g.cs", builder.ToString());
        }
        
        private static void GetEntityTypesMethod(SourceCodeBuilder builder, List<EntityTypeInfo> entityTypeInfos)
        {
            builder.AddXmlComment("All Entity Types");
            builder.BeginMethod("public List<Type> GetEntityTypes()");

            try
            {
                if (entityTypeInfos.Any())
                {
                    builder.AppendLine($"return new List<Type>({entityTypeInfos.Count})");
                    builder.AppendLine("{");
                    builder.Indent();
                    foreach (var system in entityTypeInfos)
                    {
                        builder.AppendLine($"typeof({system.EntityTypeFullName}),");
                    }
                    builder.Unindent();
                    builder.AppendLine("};");
                    builder.AppendLine();
                }
                else
                {
                    builder.AppendLine($"return new List<Type>();");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            
            builder.EndMethod();
        }       

        private static bool IsEntityTarget(SyntaxNode node)
        {
            return node switch
            {
                // 普通的实体类定义
                ClassDeclarationSyntax classDecl => classDecl.BaseList != null && classDecl.BaseList.Types.Any(),
                // 泛型写法
                GenericNameSyntax => true,
                _ => false
            };
        }

        private static EntityTypeInfo? GetEntityTypeInfo(GeneratorSyntaxContext context)
        {
            var node = context.Node;
            INamedTypeSymbol? symbol = null;

            switch (node)
            {
                case ClassDeclarationSyntax classDecl:
                {
                    symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
                    break;
                }
                case GenericNameSyntax genericName:
                {
                    // 获取泛型名称对应的类型符号
                    var symbolInfo = context.SemanticModel.GetSymbolInfo(genericName);
                    symbol = symbolInfo.Symbol as INamedTypeSymbol;
                    break;
                }
            }

            if (symbol == null)
            {
                return null;
            }
            
            if (!InheritsFromEntitySymbol(symbol))
            {
                // 排除非实体
                return null;
            }
            
            if (!symbol.IsInstantiable())
            {
                // 排除不可实例化的
                return null;
            }

            return new EntityTypeInfo(
                symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
                symbol.Name );
        }

        private sealed record EntityTypeInfo(
            string EntityTypeFullName,
            string EntityTypeName
        );

        /// <summary>
        /// 检查符号是否继承自 Entity
        /// </summary>
        public static bool InheritsFromEntitySymbol(INamedTypeSymbol? symbol)
        {
            var current = symbol;
            while (current != null)
            {
                // 基类是否是Fantasy.Entitas.Entity
                if (current.Name == "Entity" && current.Arity == 0)
                {
                    if (current.ContainingNamespace.ToDisplayString() == "Fantasy.Entitas") 
                        return true;
                }

                current = current.BaseType;
            }
            return false;
        }
    }
}